from flask import Flask, render_template
from learn_flask.extensions import db, login_manager
from learn_flask.models import User
from learn_flask.commands import register_commands
from sqlalchemy import select


def create_app(config_name='development'):
    """
    应用工厂函数，用于创建和配置Flask应用实例
    
    Args:
        config_name (str): 配置名称，默认为'development'
        
    Returns:
        Flask: 配置好的Flask应用实例
        
    应用工厂模式的优势：
    1. 可以创建多个应用实例，便于测试
    2. 不同环境可以使用不同配置
    3. 避免循环导入问题
    """
    # 创建Flask应用实例
    # 指定模板和静态文件目录，因为它们位于项目根目录而不是learn_flask包内
    app = Flask(__name__, template_folder='../templates', static_folder='../static')
    
    # 根据配置名称加载对应配置类
    from learn_flask.settings import config
    app.config.from_object(config[config_name])

    # 初始化扩展
    # SQLAlchemy数据库扩展
    db.init_app(app)
    # Flask-Login用户认证扩展
    login_manager.init_app(app)
    # 设置登录视图端点，未登录用户访问受限页面时重定向到这里
    login_manager.login_view = 'auth.login'
    # 设置登录提示信息
    login_manager.login_message = '请先登录'

    # 用户加载回调函数，Flask-Login通过此函数根据用户ID获取用户对象
    @login_manager.user_loader
    def load_user(user_id):
        """
        Flask-Login必需的用户加载回调函数
        
        Args:
            user_id (int): 用户唯一标识符
            
        Returns:
            User: 对应的用户对象，如果不存在返回None
        """
        return db.session.get(User, int(user_id))

    # 注册蓝本（Blueprint）
    # 主要业务逻辑蓝本
    from learn_flask.blueprints.main import main_bp
    # 用户认证相关蓝本
    from learn_flask.blueprints.auth import auth_bp
    
    # 将蓝本注册到应用
    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp)

    # 注册模板上下文处理器，在所有模板中都可以使用user变量
    @app.context_processor
    def inject_user():
        """
        模板上下文处理器，向所有模板注入user变量
        
        Returns:
            dict: 包含user键值对的字典
        """
        user = db.session.execute(select(User)).scalar()
        return dict(user=user)

    # 注册错误处理程序
    @app.errorhandler(404)
    def page_not_found(error):
        """
        404错误处理程序
        
        Args:
            error: 错误对象
            
        Returns:
            tuple: (渲染的模板, HTTP状态码)
        """
        return render_template('404.html'), 404

    # 注册自定义命令
    register_commands(app)

    return app